﻿ using System;
using System.Xml;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using System.Globalization;
using System.Configuration;

namespace Postcode.Settings
{
    public class PostcodeProperties
    {
        private static List<PostcodeProperty> _countryProperties = null;

        /// <summary>
        /// Creates a PostcodeProperties object with the cached properties,
        /// when no properties have been set yet it will load them.
        /// </summary>
        public PostcodeProperties()
        {
            this.init(false);
        }
        /// <summary>
        /// Creates a PostcodeProperties object but empties the cache and
        /// rebuilds it
        /// </summary>
        /// <param name="EmptyCache"></param>
        public PostcodeProperties(bool EmptyCache)
        {
            this.init(EmptyCache);
        }
        /// <summary>
        /// Fetches the postcode property based on the given region
        /// </summary>
        /// <param name="Region">The region we need to fetch the postcode property for</param>
        public PostcodeProperty Get(RegionInfo Region)
        {
            IEnumerable<PostcodeProperty> props = PostcodeProperties._countryProperties.Where(country => country.ISOName == Region.TwoLetterISORegionName);
            
            // only return something when the number of properties found is equal to 1
            if (props.Count() == 1)
            {
                return props.First();
            }

            throw new Exception("Invalid Postcode Region found");
        }

        /// <summary>
        /// The init function builds the PostcodeProperties object and fills it
        /// if needed
        /// </summary>
        /// <param name="EmptyCache">Do we need to empty the cache?</param>
        private void init(bool EmptyCache)
        {
            if (EmptyCache)
            {   // if we want to empty the cache do it
                PostcodeProperties._countryProperties = null;
            }

            if (PostcodeProperties._countryProperties == null)
            {
                // create an empty list of type PostcodeProperty
                PostcodeProperties._countryProperties = new List<PostcodeProperty>();
                // fill it with the xml data
                this.loadXml(this.getXmlFile());
            }
        }

        /// <summary>
        /// Load the xml and transform it into PostcodeProperty objects
        /// </summary>
        /// <param name="reader">The xml reader needs to point to a valid xml file</param>
        private void loadXml(XmlTextReader reader)
        {
            while (reader.ReadToFollowing("Country"))
            {
                using (XmlReader subtreeReader = reader.ReadSubtree())
                {
                    PostcodeProperties._countryProperties.Add(
                        new PostcodeProperty(subtreeReader) // create a new PostcodeProperty 
                                                            // object and let it read the needed
                                                            // data from the subtree
                    );
                }
            }
        }

        /// <summary>
        /// Get the xml file, validate it and return a XmlTextReader object
        /// </summary>
        /// <returns></returns>
        private XmlTextReader getXmlFile()
        {
            // based on the application settings we take the build in
            // xml resource or we use the application settings
            String countryXml = Resources.Postcode.Countries;
            if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["PostcodeOverrideFile"])) 
            {
                try
                {
                    countryXml = File.ReadAllText(ConfigurationManager.AppSettings["PostcodeOverrideFile"]);
                }
                catch { throw new Exception("Invalid file given, file does not exist on the given path."); }
            }

            byte[] byteArray = System.Text.Encoding.ASCII.GetBytes(countryXml);
            Stream xml_stream = new MemoryStream(byteArray);

            return new XmlTextReader(xml_stream);
        }
    }
}
